package com.heima.lock.controller;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.heima.lock.mapper.GoodsMapper;
import com.heima.lock.mapper.OrderMapper;
import com.heima.lock.mapper.UserMapper;
import com.heima.lock.pojos.Goods;
import com.heima.lock.pojos.Order;
import com.heima.lock.pojos.User;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.SimpleTransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * 高并发操作： 购物秒杀场景
 */
@RestController
@RequestMapping("/shop")
@Slf4j
public class ShopController {

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private TransactionTemplate transactionTemplate;

    @PostMapping
    public String shopping(@RequestBody Map map) {
        Integer goodsId = (Integer) map.get("goodsId");
        Integer goodsCount = (Integer) map.get("goodsCount");
        Integer userId = (Integer) map.get("userId");

        //1. 下单  -- 创建订单
        Order order = new Order();

        String orderNo = new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
        String idWorker = String.valueOf(IdWorker.getId());
        orderNo = orderNo + idWorker.substring(idWorker.length() - 5);
        order.setOrderNo(orderNo);         //20231202151612345
        order.setGoodsId(goodsId);
        order.setUserId(userId);

        RLock lock = redissonClient.getLock("LOCK");

        lock.lock();

        //手动管理事物  -- 开启事物
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus transactionStatus) {
                // 在这里执行需要在事务中运行的操作
                try {
                    // 执行你的业务逻辑
                    Goods goods = goodsMapper.selectById(goodsId);
                    BigDecimal price = goods.getPrice();
                    BigDecimal orderAmount = price.multiply(new BigDecimal(String.valueOf(goodsCount)));
                    order.setAmount(orderAmount);
                    order.setCreateTime(new Date());
                    order.setUpdateTime(new Date());
                    orderMapper.insert(order);


                    //2. 扣商品库存
                    Integer stock = goods.getStock();
                    stock -= goodsCount;
                    if (stock < 0) {
                        throw new RuntimeException("下单失败，库存不足!");
                    }

                    goods.setStock(stock);
                    goodsMapper.updateById(goods);

                    //3. 修改用户余额
                    User user = userMapper.selectById(userId);
                    BigDecimal money = user.getMoney();
                    money = money.subtract(orderAmount);
                    if (money.doubleValue() < 0) {
                        throw new RuntimeException("下单失败，账户余额不足!");
                    }

                    user.setMoney(money);
                    userMapper.updateById(user);

                    log.info(userId + "号用户下单成功，购买：" + goodsId + "号商品，下单数量：" + goodsCount);

                    //手动管理事物  -- 提交事物
                    transactionStatus.flush();
                } catch (Exception e) {
                    // 发生异常时回滚事务
                    transactionStatus.setRollbackOnly();
                }
                return null;
            }
        });

        lock.unlock();
        return "success";
    }

}
